import numpy as np
from GDHSIndividual import HSIndividual
import random
import copy
import  time
import matplotlib.pyplot as plt
import math

class GDHSImprovedHarmonySearch:

    '''
    the class for harmony search algorithm
    '''

    def __init__(self, sizepop, vardim, bound, MAXGEN,function):
        '''
        sizepop: population sizepop
        vardim: dimension of variables
        bound: boundaries of variables
        MAXGEN: termination condition

        '''
        self.sizepop = sizepop
        self.vardim = vardim
        self.bound = bound
        self.MAXGEN = MAXGEN
        self.function = function


        self.population = []
        self.fitness = np.zeros((self.sizepop, 1))
        self.trace = []

        self.U = []
        self.L = []

    def initialize(self):
        '''
        initialize the population of hs
        '''
        for i in range(0, self.sizepop):
            ind = HSIndividual(self.vardim, self.bound,self.function)
            ind.generate()
            self.population.append(ind)

    def evaluation(self):
        '''
        evaluation the fitness of the population   求解向量对应的解
        '''
        for i in range(0, self.sizepop):
            self.population[i].calculateFitness()
            self.fitness[i] = self.population[i].fitness

    def improvise(self,HMCR,PAR,bw,t,U,L):
        '''
        improvise a new harmony     即兴创作
        '''


        U = U+bw
        L = L-bw

        if U >self.bound[1,0]:
            U = self.bound[1,0]

        if L < self.bound[0,0]:
            L = self.bound[0,0]
        bound = np.tile([[L],[U]],self.vardim)
        ind = HSIndividual(self.vardim, bound, self.function)
        for i in range(0,self.vardim):
            r1 = random.random()
            r2 = random.random()
            if r1<HMCR:
                if r2>PAR:
                    ind.chrom[i]=\
                    self.population[random.randint(0,self.sizepop-1)].chrom[i]
                else:

                    if random.random() > 0.5:
                        J = random.randint(0, self.sizepop - 1)
                        coef = (1 + (self.sizepop - J)) * (1 - (t - 1) / (self.MAXGEN - 1))
                        ind.chrom[i]= \
                            self.population[J].chrom[i]+\
                            coef*bw
                    else:
                        J = random.randint(0, self.sizepop - 1)
                        coef = (1+(self.sizepop - J))*(1 - (t-1)/(self.MAXGEN-1))
                        ind.chrom[i]= \
                            self.population[J].chrom[i]-\
                            coef*bw

            else:
                ind.chrom[i] = L + \
                                (U - L) * random.random()

        ind.calculateFitness()

        return ind


    def update(self, ind):
        '''
        update harmony memory
        '''
        maxIdx = np.argmax(self.fitness)
        if ind.fitness < self.population[maxIdx].fitness:
            z = self.population[maxIdx].fitness

            self.population[maxIdx] = ind
            self.fitness[maxIdx] = ind.fitness




    def solve(self):
        '''
        the evolution process of the hs algorithm
        '''
        print("开始执行GDHS")
        start = time.time()
        self.t = 0
        self.initialize()
        self.evaluation()
        print(self.bound[0, 0], "====", self.bound[1, 1])

        bestIndex = np.argmin(self.fitness)
        self.best = copy.deepcopy(self.population[bestIndex])
        self.avefitness = np.mean(self.fitness)
        bw_den = 20*abs(1+(math.log10(self.bound[1,0]-self.bound[0,0])))

        while self.t < self.MAXGEN - 1:
            self.t += 1
            self.U.clear()
            self.L.clear()
            for i in range(self.sizepop):
                MAX = np.argmax(self.population[i].chrom)
                MIN = np.argmin(self.population[i].chrom)
                self.U.append(self.population[i].chrom[MAX])
                self.L.append(self.population[i].chrom[MIN])
            U = np.max(self.U)
            L = np.min(self.L)

            HMCR = 0.9+0.2*((self.t-1)/(self.MAXGEN-1)*(1-(self.t-1)/(self.MAXGEN-1)))**0.5
            PAR = 0.85+0.3*((self.t-1)/(self.MAXGEN-1)*(1-(self.t-1)/(self.MAXGEN-1)))**0.5

            bwmax = (U-L)/bw_den
            bwmin = 0.001*bwmax

            bw = bwmax * math.exp((math.log(0.001))*((self.t-1)/(self.MAXGEN-1)))

            ind = self.improvise(HMCR,PAR,bw,self.t,U,L)
            self.update(ind)


            bestIndex = np.argmin(self.fitness)
            self.best = self.population[bestIndex]
            self.avefitness = np.mean(self.fitness)
            self.trace.append(self.best.fitness)

        end = time.time()

        print("==========")
        print("time:",end-start)
        print("GDHSbest:",self.best.fitness)
        print("=================")
        #self.printResult()

